package com.glsc.ngateway.opmanage.aspect;

import com.glsc.ngateway.common.base.annotation.EncryptField;
import com.glsc.ngateway.common.base.enums.EncryptTypeEnum;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * Hibernate拦截器，加解密
 */
@Component
public class HibernateInterceptor extends EmptyInterceptor {

    private final static Logger LOGGER = LoggerFactory.getLogger(HibernateInterceptor.class);

    private final static String TYPE_FLUSH = "onFlushDirty";
    private final static String TYPE_LOAD = "onLoad";
    private final static String TYPE_SAVE = "onSave";

    @Override
    public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {

    }

    @Override
    public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
        Object[] newState = dealField(entity, currentState, propertyNames, TYPE_FLUSH);
        return super.onFlushDirty(entity, id, newState, previousState, propertyNames, types);
    }

    @Override
    public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        Object[] newState = dealField(entity, state, propertyNames, TYPE_LOAD);
        return super.onLoad(entity, id, newState, propertyNames, types);
    }

    @Override
    public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
        Object[] newState = dealField(entity, state, propertyNames, TYPE_SAVE);
        return super.onSave(entity, id, newState, propertyNames, types);
    }

    /**
     * 处理字段，加解密
     *
     * @param entity        实体类
     * @param state         数据
     * @param propertyNames 字段名称
     * @param type          方法类型
     * @return
     */
    private Object[] dealField(Object entity, Object[] state, String[] propertyNames, String type) {
        Map<String, EncryptTypeEnum> annotationFields = getAnnotationField(entity);
        if (annotationFields.isEmpty()) {
            return state;
        }
        LOGGER.info("调用方法:{}, 需要加/解密的字段:{}", type, annotationFields);
        Map<String, Integer> propertyNameMap = new HashMap<>();
        for (int i = 0; i < propertyNames.length; i++) {
            propertyNameMap.put(propertyNames[i], i);
        }
        //遍历字段名和加解密字段名
        annotationFields.forEach((field, encryptType) -> {
            if (propertyNameMap.containsKey(field)) {
                int i = propertyNameMap.get(field);
                if (state[i] != null && !Objects.equals(state[i].toString(), "")) {
                    switch (type) {
                        case TYPE_SAVE:
                        case TYPE_FLUSH:
                            try {
                                String encryptStr = encryptType.encrypt((String) state[i]);
//                                LOGGER.info("【加密】字段:{}, 加密前:{}, 加密后:{}", field, state[i], encryptStr);
                                state[i] = encryptStr;
                                break;
                            } catch (Exception e) {
                                LOGGER.error(String.format("加密:%s,失败:%s", (String) state[i], e.getMessage()));
                                throw new RuntimeException("加密失败");
                            }
                        case TYPE_LOAD:
                            try {
                                String decryptStr = encryptType.decrypt((String) state[i]);
//                                LOGGER.info("【解密】字段:{}, 解密前:{}, 解密后:{}", field, state[i], decryptStr);
                                state[i] = decryptStr;
                                break;
                            } catch (Exception e) {
                                LOGGER.error(String.format("解密:%s,失败:%s", (String) state[i], e.getMessage()));
                                throw new RuntimeException("解密失败");
                            }
                        default:
                            LOGGER.error(String.format("未匹配到数据操作：%s", type));
                            break;
                    }
                }
            }
        });
        return state;
    }

    /**
     * 获取实体类中带有注解的变量名
     *
     * @param entity 实体类
     * @return 需要加解密的字段
     */
    private Map<String, EncryptTypeEnum> getAnnotationField(Object entity) {
        Class<?> entityClz = entity.getClass();
        Map<String, EncryptTypeEnum> fields = new HashMap<>();
        for (Field field : entityClz.getDeclaredFields()) {
            EncryptField encryptField = field.getAnnotation(EncryptField.class);
            if (Objects.isNull(encryptField)) {
                continue;
            }
            fields.put(field.getName(), encryptField.type());
        }
        return fields;
    }
}
